package com.viknix.threadpool.manager.server.dependence.monitor.publisher;

import cn.hutool.core.collection.ConcurrentHashSet;
import com.viknix.threadpool.manager.server.dependence.monitor.NotifyCenter;
import com.viknix.threadpool.manager.server.dependence.monitor.event.Event;
import com.viknix.threadpool.manager.server.dependence.monitor.listeners.Listener;
import com.viknix.threadpool.manager.server.dependence.util.CollectionUtils;
import com.viknix.threadpool.manager.server.dependence.util.ThreadUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * @Author: Dongqi
 * @Date: 2021/11/2 10:42
 * @Version 1.0
 * @Description: 默认多播器：实际上就是一个线程，不断从 eventQueue 中获取事件，并且逐个回调监听器的方法
 */
@Slf4j
public class DefaultPublisher extends Thread implements EventPublisher {

    private volatile boolean initialized = false;

    private volatile boolean shutdown = false;

    /**
     * 该 publisher 关注的事件类型
     */
    private Class<? extends Event> eventType;

    /**
     * 用于存放该事件的订阅者 (慢事件也会将所有监听器添加到这个属性当中)
     */
    protected final ConcurrentHashSet<Listener> listeners = new ConcurrentHashSet();

    /**
     * 队列容量
     */
    private int queueMaxSize = -1;

    /**
     * 用于缓存发布的事件源
     */
    private BlockingQueue<Event> eventQueue;

    /**
     * 最终执行的事件序列（只有当监听器没有忽略过期事件，该属性才会有用）
     */
    protected volatile Long lastEventSequence = -1L;

    /**
     * lastEventSequence 属性更新器
     */
    private static final AtomicReferenceFieldUpdater<DefaultPublisher, Long> UPDATER = AtomicReferenceFieldUpdater
            .newUpdater(DefaultPublisher.class, Long.class, "lastEventSequence");

    /**
     * 初始化方法：每次再多播工厂生产完新的多播器后，会同时初始化该多播器
     *
     * @param type       该多播器关注的事件类型
     * @param bufferSize 阻塞队列最大容量
     */
    @Override
    public void init(Class<? extends Event> type, int bufferSize) {
        setDaemon(true);
        setName("default-publisher");
        this.eventType = type;
        this.queueMaxSize = bufferSize;
        this.eventQueue = new ArrayBlockingQueue(bufferSize);
        // 启动当前多播器线程，循环从 eventQueue 中获取并处理事件
        start();
    }

    public Class<? extends Event> getEventType() {
        return eventType;
    }

    public ConcurrentHashSet<Listener> getSubscribers() {
        return listeners;
    }

    @Override
    public synchronized void start() {
        if (!initialized) {
            super.start();
            if (queueMaxSize < 0) {
                queueMaxSize = NotifyCenter.ringBufferSize;
            }
            initialized = true;
        }
    }

    @Override
    public long currentEventSize() {
        return eventQueue.size();
    }

    @Override
    public void run() {
        // 处理事件
        openEventHandler();
    }

    void openEventHandler() {

        try {

            int waitTimes = 60;

            for (; ; ) {
                if (shutdown || hasSubscriber() || waitTimes <= 0) {
                    break;
                }
                ThreadUtils.sleep(1000L);
                waitTimes--;
            }

            for (; ; ) {
                if (shutdown) {
                    break;
                }
                // 阻塞队列出队
                final Event event = eventQueue.take();
                // 由当前多播器线程执行该事件，回调监听器方法
                receiveEvent(event);
                // 更新 lastEventSequence 值为当前最大的事件序列（只有当监听器没有忽略过期事件的时候，该行为才有意义）
                UPDATER.compareAndSet(this, lastEventSequence, Math.max(lastEventSequence, event.sequence()));
            }
        } catch (Throwable e) {
            e.printStackTrace();
            log.error(">>> 😒 Event listener exception :: {}", e);
        }
    }

    /**
     * 判断该多播器中是否有监听器对象
     *
     * @return true：有   false：没有
     */
    private boolean hasSubscriber() {
        return CollectionUtils.isNotEmpty(listeners);
    }

    @Override
    public void addSubscriber(Listener listener) {
        listeners.add(listener);
    }

    @Override
    public void removeSubscriber(Listener listener) {
        listeners.remove(listener);
    }

    @Override
    public boolean publish(Event event) {
        checkIsStart();
        // 将事件对象放入阻塞队列中
        boolean success = this.eventQueue.offer(event);
        if (!success) {
            log.warn(">>> 😒 Unable to plug in due to interruption, synchronize sending time, event :: {}", event);
            // 如果放不下直接由当前线程执行该事件
            receiveEvent(event);
            return true;
        }
        return true;
    }

    void checkIsStart() {
        if (!initialized) {
            throw new IllegalStateException("Publisher does not start");
        }
    }

    /**
     * 清空队列，并且不再处理队列中的任务
     */
    @Override
    public void shutdown() {
        this.shutdown = true;
        this.eventQueue.clear();
    }

    public boolean isInitialized() {
        return initialized;
    }

    /**
     * 依次回调监听器方法
     *
     * @param event 事件源对象
     */
    void receiveEvent(Event event) {
        final long currentEventSequence = event.sequence();

        // 遍历该事件的所有监听者，依次调用事件监听方法
        for (Listener listener : listeners) {

            // 是否忽略过期事件，如果忽略过期事件，那么当前的事件序列如果小于最终执行的事件序列，那么会忽略当前监听器的回调方法，不调用该事件
            if (listener.ignoreExpireEvent() && lastEventSequence > currentEventSequence) {
                log.debug(">>> [😒 NotifyCenter] the {} is unacceptable to this subscriber, because had expire",
                        event.getClass());
                continue;
            }
            // 通知订阅者
            notifySubscriber(listener, event);
        }
    }

    @Override
    public void notifySubscriber(Listener listener, Event event) {

        // 构建任务，回调监听方法
        final Runnable job = () -> listener.onEvent(event);
        // 获取监听器内部的线程池
        final Executor executor = listener.executor();
        // 如果监听器内部的线程池不为空
        if (executor != null) {
            // 由监听器内部的线程池执行任务
            executor.execute(job);
        } else {
            try {
                // 如果监听器内部的线程池为空，那么由当前线程执行回调方法
                job.run();
            } catch (Throwable e) {
                log.error(">>> Event callback exception :: {}", e);
            }
        }
    }
}
